package com.panopset.app.swiftwheel;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;

import javax.swing.JPanel;
import javax.swing.JSplitPane;

import com.panopset.Util;
import com.panopset.compat.Strings;
import com.panopset.compat.Zombie;
import com.panopset.flywheel.Flywheel;
import com.panopset.sf.PanTextField;
import com.panopset.sf.TitledTextArea;
import com.panopset.sf.Windoc;
import com.panopset.sf.WindocField;

/**
 * Swiftwheel user input.
 *
 * @author Karl Dinwiddie
 */
public final class UserInput {
    private Windoc w;

    public UserInput(final Windoc windoc) {
        w = windoc;
    }

    /**
     * Flywheel prompter.
     */
    private FlywheelPrompter fp;

    /**
     *
     * @return Prompter.
     */
    private FlywheelPrompter getPrompter() {
        if (fp == null) {
            fp = new FlywheelPrompter(w);
            fp.setTextInjectListener(new TextInjectListener() {

                @Override
                public void inject(String text) {
                    getTemplateTA().getTextArea().insert(text,
                            getTemplateTA().getTextArea().getCaretPosition());
                    circumstancesChanged = true;
                }

            });
        }
        return fp;
    }

    /**
     * Create map from input line.
     *
     * @param s
     *            Input line.
     * @return Map.
     */
    private Map<String, String> createMapFrom(final String s) {
        Map<String, String> rtn = new HashMap<String, String>();
        rtn.put("l", s);

        if (Strings.isPopulated(s)) {
            int i = 0;
            StringTokenizer st = new StringTokenizer(s, " ");
            while (st.hasMoreTokens()) {
                rtn.put("w" + i++, st.nextToken());
            }
            if (Strings.isPopulated(getTokensTF().getText())) {
                i = 0;
                st = new StringTokenizer(s, getTokensTF().getText());
                while (st.hasMoreTokens()) {
                    rtn.put("t" + i++, st.nextToken());
                }
            }
        }
        return rtn;
    }

    /**
     * If circumstances have changed, this method will update the output.
     */
    private synchronized void update() {
        StringWriter sw = new StringWriter();

        for (String s : Strings.getLines(getInputTA().getText())) {
            Flywheel fw = new Flywheel.Builder().map(createMapFrom(s))
                    .input(Strings.getLines(getTemplateTA().getText()))
                    .construct();
            getPrompter().setFlywheel(fw);
            sw.append(fw.exec());
            sw.append(Strings.getEol());
        }

        getOutputTA().setText(sw.toString());
    }

    /**
     * Check to see if we need to do an update.
     */
    private void check() {
        if (circumstancesChanged) {
            update();
            circumstancesChanged = false;
        }
    }

    /**
     * Circumstances changed flag.
     */
    private boolean circumstancesChanged = true;

    /**
     * Input text area.
     */
    private TitledTextArea inputTA;

    /**
     * Circumstances changed key adapter.
     */
    private class CircumstancesChangedAdapter extends KeyAdapter {
        @Override
        public void keyPressed(final KeyEvent e) {
            circumstancesChanged = true;
        }

    }

    private CircumstancesChangedAdapter circumstancesChangedAdapter;

    private CircumstancesChangedAdapter getCircumstancesChangedAdapter() {
        if (circumstancesChangedAdapter == null) {
            circumstancesChangedAdapter = new CircumstancesChangedAdapter();
        }
        return circumstancesChangedAdapter;
    }

    /**
     * @return Input text area.
     */
    private TitledTextArea getInputTA() {
        if (inputTA == null) {
            inputTA = w.cf.createTitledTextArea(w, Util.x("Input"), INPUT_TTT,
                    "Input");
            inputTA.addKeyListener(getCircumstancesChangedAdapter());
        }
        return inputTA;
    }

    /**
     * Input text area.
     */
    private TitledTextArea templateTA;

    /**
     * @return Input text area.
     */
    private TitledTextArea getTemplateTA() {
        if (templateTA == null) {
            templateTA = w.cf.createTitledTextArea(w, Util.x("Template"),
                    TEMPLATE_TTT, "template");
            templateTA.addKeyListener(getCircumstancesChangedAdapter());
        }
        return templateTA;
    }

    /**
     * Output text area.
     */
    private TitledTextArea outputTA;

    /**
     * @return Output text area.
     */
    private TitledTextArea getOutputTA() {
        if (outputTA == null) {
            outputTA = w.cf.createTitledTextArea(w, Util.x("Output"),
                    OUTPUT_TTT, "");
        }
        return outputTA;
    }

    /**
     * Tokens text field.
     */
    private PanTextField tokensTF;

    /**
     * @return Tokens text field.
     */
    private PanTextField getTokensTF() {
        if (tokensTF == null) {
            tokensTF = w.cf.createTitledField(w, 6, Util.x("Tokens"), Util
                    .x("Separate input lines in to "
                            + "w variables by these tokens.  "
                            + "Space is used if blank."), "tokens");
            tokensTF.getTF().addKeyListener(getCircumstancesChangedAdapter());
        }
        return tokensTF;
    }

    /**
     * Main Swiftwheel panel.
     */
    private JPanel panel;

    /**
     * Default divider location is 80D.
     */
    private static final int DFT_DL = 500;

    /**
     * @return Main Swiftwheel panel.
     */
    public JPanel getPanel() {
        if (panel == null) {
            final JSplitPane jsp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
            new WindocField(w, "" + DFT_DL, "divloc") {

                @Override
                public void setGuiValue(final String s) {
                    if (Strings.isPopulated(s)) {
                        try {
                            jsp.setDividerLocation(Integer.parseInt(s));
                        } catch (NumberFormatException ex) {
                            jsp.setDividerLocation(DFT_DL);
                        }
                    } else {
                        jsp.setDividerLocation(DFT_DL);
                    }
                    jsp.invalidate();
                }

                @Override
                public String getGuiValue() {
                    return "" + jsp.getDividerLocation();
                }

            };
            jsp.setDividerLocation(DFT_DL);
            panel = new JPanel(new GridLayout(1,1));
            panel.add(jsp);
            JPanel fwp = w.cf.createPanel(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.weightx = 1;
            gbc.weighty = 1;
            gbc.fill = GridBagConstraints.BOTH;
            fwp.add(getInputTA().getPanel(), gbc);
            gbc.gridy++;
            fwp.add(getTemplateTA().getPanel(), gbc);
            gbc.gridy++;
            fwp.add(getOutputTA().getPanel(),

            gbc);
            gbc.gridy++;
            gbc.weighty = 0;
            fwp.add(getTokensTF().getPanel(), gbc);
            new Thread(new Runnable() {

                @Override
                public void run() {
                    while (Zombie.isActive()) {
                        try {
                            Thread.sleep(getSleepTime());
                            check();
                        } catch (InterruptedException ex) {
                            Util.log(ex);
                        }
                    }
                }
            }, "Updater").start();
            panel.add(fwp);
            panel.add(getPrompter().getPanel());
        }
        return panel;
    }

    /**
     * Flywheel template input.
     */
    private static final String INPUT_TTT = Util.x("Flywheel template input.");

    /**
     * Flywheel template.
     */
    private static final String TEMPLATE_TTT = Util.x("Flywheel template.");

    /**
     * Flywheel template output.
     */
    private static final String OUTPUT_TTT = Util
            .x("Results of transformation.");

    /**
     * Default sleep time is 500 milliseconds, or a half second.
     */
    private static final Integer DEFAULT_SLEEP_TIME = 500;

    /**
     * Sleep time. Increase for more complicated templates.
     */
    private Integer sleepTime;

    /**
     * @return Sleep time.
     */
    public Integer getSleepTime() {
        if (sleepTime == null) {
            sleepTime = DEFAULT_SLEEP_TIME;
        }
        return sleepTime;
    }

    /**
     * @param newSleepTime
     *            New sleep time, in milliseconds (1000 = 1 second).
     */
    public void setSleepTime(final int newSleepTime) {
        sleepTime = newSleepTime;
    }
}
